import std from './dxStd.js'
import dxUart from './dxUart.js'
import dxCommon from './dxCommon.js'
import log from './dxLogger.js'

const dxFingerZaz = {}

let options = {
    id : 'fingerUart',
    total: 5000,
    timeout : 500,
    type : '3',
    path : '/dev/ttySLB1',
    baudrate : '115200-8-N-1'
}


/**
 * Test fingerprint module command
 * @returns {boolean} Returning true indicates the test succeeded, and returning false indicates the test failed.
 */
dxFingerZaz.test = function () {
    send({prefix: 0x55AA, cmd: 0x0100, data: ''})
    let resp = receive({prefix: 0xAA55, cmd: 0x0100})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.test:", resp)
        return false
    }
}


/**
 * Get the first registrable number within the specified number range.
 * @param {number} startId - Starting number
 * @param {number} endId - End number
 * @returns {number} Returns an available fingerprint number; returns false if the ret attempt failed (the reason for failure can be found in the ret function).
 */
dxFingerZaz.getEmptyId = function (startId, endId) {
    if(startId === undefined || startId === null || startId < 1 || startId > options.total){
        throw new Error("dxFingerZaz.getEmptyId: 'startId' parameter should be between 1 and " + options.total)
    }
    if(endId === undefined || endId === null || endId < 1 || endId > options.total){
        throw new Error("dxFingerZaz.getEmptyId: 'endId' parameter should be between 1 and " + options.total)
    }

    let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2)
    send({prefix: 0x55AA, cmd: 0x4500, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4500})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data, 2)
    } else {
        log.error("dxFingerZaz.getEmptyId:", resp)
        return false
    }
}


/**
 * Get the registration status of the template with the specified number
 * @param {number} keyId - fingerprint number
 * @returns {number} A return value of 0 indicates that the application is not registered, 1 indicates that it is registered, and false indicates that the application failed to be registered.
 */
dxFingerZaz.getStatus = function (keyId) {
    if (keyId === undefined || keyId === null) {
        throw new Error("dxFingerZaz.getStatus: 'keyId' parameter should not be null or empty")
    }

    send({prefix: 0x55AA, cmd: 0x4600, data: toEndianHexExtended(keyId, 2)})
    let resp = receive({prefix: 0xAA55, cmd: 0x4600})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data, 1)
    } else {
        log.error("dxFingerZaz.getStatus:", resp)
        return false
    }
}


/**
 * Collect fingerprint images
 * @returns {boolean} Returning true indicates successful data collection, while returning false indicates failure (the reason for failure can be found in ret).
 */
dxFingerZaz.getImage = function () {
    send({prefix: 0x55AA, cmd: 0x2000, data: ""})
    let resp = receive({prefix: 0xAA55, cmd: 0x2000})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.getImage:", resp)
        return false
    }
}


/**
 * Generate a template from the fingerprint image temporarily stored in the ImageBuffer.
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @returns {boolean} Returning true indicates successful generation, while returning false indicates failure (the reason for failure can be found in ret).
 */
dxFingerZaz.generate = function (bufferNum) {
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.generate: 'bufferNum' parameter should be between 0 and 3")
    }
    send({prefix: 0x55AA, cmd: 0x6000, data: toEndianHexExtended(bufferNum, 2)})
    let resp = receive({prefix: 0xAA55, cmd: 0x6000})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.generate:", resp)
        return false
    }
}


/**
 * Merge fingerprint templates
 * @param {number} mergeCount - Number of templates to merge (2|3): 2: merge Ram Buffer0 and Ram Buffer1; 3: merge Ram Buffer0, Ram Buffer1 and Ram Buffer2.
 * @param {number} bufferNum - Fingerprint merged template number (0-2)
 * @returns {boolean} Returning true indicates a successful merge, while returning false indicates a failed merge.
 */
dxFingerZaz.merge = function (mergeCount, bufferNum) {
    if(mergeCount === undefined || mergeCount === null || mergeCount < 2 || mergeCount > 3){
        throw new Error("dxFingerZaz.merge: 'mergeCount' parameter should be between 2 and 3")
    }
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.merge: 'bufferNum' parameter should be between 0 and 3")
    }

    let data = toEndianHexExtended(bufferNum, 2) + toEndianHexExtended(mergeCount, 1)
    send({prefix: 0x55AA, cmd: 0x6100, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x6100})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.merge:", resp)
        return false
    }
}


/**
 * Save fingerprint template data to the module fingerprint library
 * @param {number} keyId - fingerprint number
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @param {boolean} [overwrite=false] - Should existing fingerprints be overwritten?
 * @returns {boolean} Returning true indicates successful storage, and returning false indicates storage failure.
 */
dxFingerZaz.storeChar = function (keyId, bufferNum, overwrite = false) {
    if(keyId === undefined || keyId === null){
        throw new Error("dxFingerZaz.storeChar: 'keyId' parameter should be between 0 and 255")
    }
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.storeChar: 'bufferNum' parameter should be between 0 and 3")
    }
    if (overwrite) {
        const delRet = dxFingerZaz.delChar(keyId, keyId)
        if (!delRet) {
            log.error("dxFingerZaz.storeChar: failed to delete existing fingerprint for keyId", keyId)
            return false
        }
    }
    let data = toEndianHexExtended(keyId, 2) + toEndianHexExtended(bufferNum, 2)
    send({prefix: 0x55AA, cmd: 0x4000, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4000})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.storeChar:", resp)
        return false
    }
}


/**
 * The fingerprint in the read module is temporarily stored in the RamBuffer.
 * @param {number} keyId - fingerprint number
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @returns {boolean} Returning true indicates successful storage, and returning false indicates storage failure.
 */
dxFingerZaz.loadChar = function (keyId, bufferNum) {
    if(keyId === undefined || keyId === null){
        throw new Error("dxFingerZaz.loadChar: 'keyId' parameter should be between 0 and 255")
    }
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.loadChar: 'bufferNum' parameter should be between 0 and 3")
    }

    let data = toEndianHexExtended(keyId, 2) + toEndianHexExtended(bufferNum, 2)
    send({prefix: 0x55AA, cmd: 0x4100, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4100})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.loadChar:", resp)
        return false
    }
}


/** 
 * Upload the template saved in the specified Ram Buffer to the HOST.
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @returns {string} Returns device information; returning false indicates failure to retrieve information.
 */
dxFingerZaz.upChar = function (bufferNum) {
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.upChar: 'bufferNum' parameter should be between 0 and 3")
    }

    let data = toEndianHexExtended(bufferNum, 2)
    send({prefix: 0x55AA, cmd: 0x4200, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4200})
    if(resp && resp.ret == 0x00){
        log.info("resp.data:", fromEndianHexExtended(resp.data, 2))
        resp = receive({prefix: 0xA55A, cmd: 0x4200, len: fromEndianHexExtended(resp.data, 2)})
        if(resp && resp.ret == 0x00){
            return dxCommon.uint8ArrayToHexString(resp.data)
        } else {
            log.error("dxFingerZaz.upChar:", resp)
            return false
        }
    } else {
        log.error("dxFingerZaz.upChar:", resp)
        return false
    }
}



/** 
 * Download the template from the host to the Ram Buffer with the specified module number.
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @param {string} template - Fingerprint template data
 * @returns {string} Returns device information; returning false indicates failure to retrieve information.
 */
dxFingerZaz.downChar = function (bufferNum, template) {
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.downChar: 'bufferNum' parameter should be between 0 and 3")
    }
    if(template === undefined || template === null){
        throw new Error("dxFingerZaz.downChar: 'template' parameter should not be null or empty")
    }
    let data = toEndianHexExtended(bufferNum, 2) + template
    log.info("template.length:", toEndianHexExtended(data.length / 2, 2))
    send({prefix: 0x55AA, cmd: 0x4300, data: toEndianHexExtended(data.length / 2, 2)})
    let resp = receive({prefix: 0xAA55, cmd: 0x4300})
    if(resp && resp.ret == 0x00){
        send({prefix: 0x5AA5, cmd: 0x4300, data: data})
        resp = receive({prefix: 0xA55A, cmd: 0x4300})
        if(resp && resp.ret == 0x00){
            return true
        } else {
            log.error("dxFingerZaz.downChar:", resp)
            return false
        }
    } else {
        log.error("dxFingerZaz.downChar:", resp)
        return false
    }
}


/** 
 * Obtain fingerprint module device information
 * @returns {string} Returns device information; returning false indicates failure to retrieve information.
 */
dxFingerZaz.getEnrolledIdList = function () {
    send({prefix: 0x55AA, cmd: 0x4900, data: ""})
    let resp = receive({prefix: 0xAA55, cmd: 0x4900})
    if(resp && resp.ret == 0x00){
        resp = receive({prefix: 0xA55A, cmd: 0x4900, len: fromEndianHexExtended(resp.data, 2)})
        if(resp && resp.ret == 0x00){
            return resp.data
        } else {
            log.error("dxFingerZaz.getDeviceInfo:", resp)
            return false
        }
    } else {
        log.error("dxFingerZaz.getDeviceInfo:", resp)
        return false
    }
}


/**
 * Search fingerprint template
 * @param {number} bufferNum - Fingerprint template number (0-2)
 * @param {number} startId - Search starting number
 * @param {number} endId - Search End Number
 * @returns {number} Returns the found fingerprint number; returns false to indicate that the search failed.
 */
dxFingerZaz.search = function (bufferNum, startId, endId) {
    if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){
        throw new Error("dxFingerZaz.search: 'bufferNum' parameter should be between 0 and 3")
    }
    if(startId === undefined || startId === null || startId < 1 || startId > options.total){
        throw new Error("dxFingerZaz.search: 'startId' parameter should be between 1 and " + options.total)
    }
    if(endId === undefined || endId === null || endId < 1 || endId > options.total){
        throw new Error("dxFingerZaz.search: 'endId' parameter should be between 1 and " + options.total)
    }

    let data = toEndianHexExtended(bufferNum, 2) + toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2)
    send({prefix: 0x55AA, cmd: 0x6300, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x6300})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data.slice(0, 2), 2)
    } else {
        log.error("dxFingerZaz.search:", resp)
        return false
    }
}


/**
 * Obtain fingerprint module parameters
 * @param {number} paramType - Parameter type (0-4) [0: Device ID, 1: Security level, 2: Repeat check, 3: Baud rate, 4: Auto-learn]
 * @returns {number} Returns the parameter value; returning false indicates failure to retrieve the value.
 * 
 * Device ID:[1-255]
 * Security level:[1-5]
 * Duplicate check:[0/1]
 * Baud rate:[1:9600, 2:19200, 3:38400, 4:57600, 5:115200, 6:230400, 7:460800, 8:921600]
 * Automatic learning:[0/1]
 */
dxFingerZaz.getParam = function (paramType) {
    if(paramType === undefined || paramType === null || paramType < 0 || paramType > 4){
        throw new Error("dxFingerZaz.getParam: 'paramType' parameter should be between 0 and 4")
    }

    let data = toEndianHexExtended(paramType, 2) 
    send({prefix: 0x55AA, cmd: 0x0300, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x0300})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data, 4)
    } else {
        log.error("dxFingerZaz.getParam:", resp)
        return false
    }
}


/**
 * Set fingerprint module parameters
 * @param {number} paramType - Parameter type (0-4) [0: Device ID, 1: Security level, 2: Repeat check, 3: Baud rate, 4: Auto-learn]
 * @param {number} paramValue - Parameter value
 * @returns {boolean} A return value of true indicates that the setting was successful, and a return value of false indicates that the setting failed.
 * 
 * Device ID:[1-255]
 * Security level:[1-5]
 * Duplicate check:[0/1]
 * Baud rate:[1:9600, 2:19200, 3:38400, 4:57600, 5:115200, 6:230400, 7:460800, 8:921600]
 * Automatic learning:[0/1]
 */
dxFingerZaz.setParam = function (paramType, paramValue) {
    if(paramType === undefined || paramType === null || paramType < 0 || paramType > 4){
        throw new Error("dxFingerZaz.setParam: 'paramType' parameter should be between 0 and 4")
    }
    if(paramValue === undefined || paramValue === null || paramValue < 0 || paramValue > 255){
        throw new Error("dxFingerZaz.setParam: 'paramValue' parameter should be between 0 and 255")
    }

    let data = toEndianHexExtended(paramType, 1) + toEndianHexExtended(paramValue, 4)
    send({prefix: 0x55AA, cmd: 0x0200, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x0200})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.setParam:", resp)
        return false
    }
}


/** 
 * Obtain fingerprint module device information
 * @returns {string} Returns device information; returning false indicates failure to retrieve information.
 */
dxFingerZaz.getDeviceInfo = function () {
    send({prefix: 0x55AA, cmd: 0x0400, data: ""})
    let resp = receive({prefix: 0xAA55, cmd: 0x0400})
    if(resp && resp.ret == 0x00){
        resp = receive({prefix: 0xA55A, cmd: 0x0400, len: fromEndianHexExtended(resp.data, 2)})
        if(resp && resp.ret == 0x00){
            return dxCommon.utf8HexToStr(dxCommon.uint8ArrayToHexString(resp.data.slice(0, resp.length - 3)))
        } else {
            log.error("dxFingerZaz.getDeviceInfo:", resp)
            return false
        }
    } else {
        log.error("dxFingerZaz.getDeviceInfo:", resp)
        return false
    }
}


/**
 * Fingerprint detection
 * @returns {number} A return value of 1 indicates fingerprint input, 0 indicates no fingerprint input, and false indicates detection failure.
 */
dxFingerZaz.fingerDetect = function () {
    send({prefix: 0x55AA, cmd: 0x2100, data: ""})
    let resp = receive({prefix: 0xAA55, cmd: 0x2100})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data, 1)
    } else {
        log.error("dxFingerZaz.fingerDetect:", resp)
        return false
    }
}


/**
 * Delete fingerprint template
 * @param {number} startId - Delete starting number
 * @param {number} endId - Delete end number
 * @returns {boolean} Returning true indicates successful deletion, while returning false indicates deletion failure.
 */
dxFingerZaz.delChar = function (startId, endId) {
    if(startId === undefined || startId === null || startId < 1 || startId > options.total){
        throw new Error("dxFingerZaz.delChar: 'startId' parameter should be between 1 and " + options.total)
    }
    if(endId === undefined || endId === null || endId < 1 || endId > options.total){
        throw new Error("dxFingerZaz.delChar: 'endId' parameter should be between 1 and " + options.total)
    }

    let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2)
    send({prefix: 0x55AA, cmd: 0x4400, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4400})
    if(resp && resp.ret == 0x00){
        return true
    } else {
        log.error("dxFingerZaz.delChar:", resp)
        return false
    }
}


/**
 * Get the total number of registered fingerprints within the specified number range.
 * @param {number} startId - Starting number
 * @param {number} endId - End number
 * @returns {number} Returns the total number of registered fingerprints within the specified number range; returns false to indicate failure.
 */
dxFingerZaz.getEnrollCount = function (startId, endId) {
    if(startId === undefined || startId === null || startId < 1 || startId > options.total){
        throw new Error("dxFingerZaz.getEnrollCount: 'startId' parameter should be between 1 and " + options.total)
    }
    if(endId === undefined || endId === null || endId < 1 || endId > options.total){
        throw new Error("dxFingerZaz.getEnrollCount: 'endId' parameter should be between 1 and " + options.total)
    }

    let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2)
    send({prefix: 0x55AA, cmd: 0x4800, data: data})
    let resp = receive({prefix: 0xAA55, cmd: 0x4800})
    if(resp && resp.ret == 0x00){
        return fromEndianHexExtended(resp.data, 2)
    } else {
        log.error("dxFingerZaz.getEnrollCount:", resp)
        return false
    }
}



/**
 * Initialize fingerprint module
 * @param {Object} params 
 * @param {string} params.type - Serial port type (default 3)
 * @param {string} params.path - Serial port path (default '/dev/ttySLB1')
 * @param {number} params.baudrate - Baud rate (default '115200-8-N-1')
 * @param {number} params.id - id (default 'fingerUart')
 * @param {number} params.total - Total number of fingerprints (default 5000)
 * @param {number} params.timeout - Timeout period (default 500)
 */
dxFingerZaz.init = function (params) {
    options.id = params.id ? params.id : options.id
    options.timeout = params.timeout ? params.timeout : options.timeout
    options.total = params.total ? params.total : options.total
    options.type = params.type ? params.type : options.type
    options.path = params.path ? params.path : options.path
    options.baudrate = params.baudrate ? params.baudrate : options.baudrate

    dxUart.open(options.type, options.path, options.id)
    dxUart.ioctl(6, options.baudrate, options.id)
}



/**
 * Convert a number to a hexadecimal string of a specified number of bytes.
 * @param {number} number - Number to be converted
 * @param {number} bytes - Number of bytes (default 2 bytes)
 * @param {boolean} isLittleEndian - Whether to use little-endian (default is true)
 * @returns {string} Converted hexadecimal string
 */
function toEndianHexExtended(number, bytes, isLittleEndian = true) {
    // Calculate the required hexadecimal digits (2 bits per byte).
    const hexLength = bytes * 2;
    let hexStr = number.toString(16).padStart(hexLength, '0');
    
    // Divided into byte arrays
    const bytesArray = [];
    for (let i = 0; i < hexLength; i += 2) {
        bytesArray.push(hexStr.substr(i, 2));
    }
    
    // Reverse the array according to byte order
    if (isLittleEndian) bytesArray.reverse();
    
    return bytesArray.join('');
}

/**
 * Convert byte arrays to numbers (endianness supported)
 * @param {Uint8Array|number[]} bytesArray - Byte arrays (such as Uint8Array or ordinary arrays)
 * @param {number} bytes - Number of bytes (must match the actual data length)
 * @param {boolean} isLittleEndian - Whether to use little-endian (default is true)
 * @returns {number} Converted numbers
 */
function fromEndianHexExtended(bytesArray, bytes, isLittleEndian = true) {
    // Parameter validation
    if (!bytesArray || bytesArray.length !== bytes) {
        throw new Error(`Invalid bytes array. Expected length: ${bytes}`);
    }

    // Handling little-endian: Reverse byte order
    const adjustedBytes = isLittleEndian 
        ? Array.from(bytesArray).reverse() 
        : Array.from(bytesArray);

    // Convert to hexadecimal string
    const hexStr = adjustedBytes
        .map(byte => byte.toString(16).padStart(2, "0"))
        .join("");

    // Convert to number (supports large integers)
    return parseInt(hexStr, 16);
}



/**
 * Send data
 * @param {Object} params 
 * @param {number} params.prefix 
 * @param {number} params.cmd 
 * @param {string} params.data 
 */
function send(params) { 
    if (!params || typeof params !== 'object') {
        throw new Error("Parameters should be an object containing prefix, cmd and data");
    }

    const { prefix, cmd, data } = params;
    const sid = 0x00;   // Default source address 0x00
    const did = 0x00;   // Default target address 0x00

    const hexParts = data.match(/[\da-f]{2}/gi) || [];

    const bytes = new Uint8Array(hexParts.map(h => parseInt(h, 16)));

    let dataBytes
    if (prefix === 0x55AA) {
        dataBytes = new Uint8Array(16);
        dataBytes.set(bytes.subarray(0, 16), 0);

    } else if (prefix === 0x5AA5) { 
        dataBytes = new Uint8Array(500);
        dataBytes.set(bytes.subarray(0, 500), 0);
    }

    // Constructing the basic data packet
    const packet = new Uint8Array(8 + dataBytes.length + 2); // Packet header (8) + data + checksum (2)
    const view = new DataView(packet.buffer);

    // Filler head
    view.setUint16(0, prefix, false);      // Big-endian PREFIX (2 bytes)
    packet[2] = sid;                       // SID (1 byte)
    packet[3] = did;                       // DID (1 byte)
    view.setUint16(4, cmd, false);         // Big-endian CMD (2 bytes)
    view.setUint16(6, hexParts.length, true); // Big-endian LEN (2 bytes)

    // Fill data
    packet.set(dataBytes, 8);

    // Calculate checksum
    let checksum = 0;
    for (let i = 0; i < 8 + dataBytes.length; i++) {
        checksum += packet[i];
    }
    checksum &= 0xFFFF; // Take the lower 16 bits

    // Fill checksum (big-endian)
    packet[8 + dataBytes.length] = checksum & 0xFF;
    packet[8 + dataBytes.length + 1] = (checksum >> 8) & 0xFF;

    // Send data
    let ret = dxUart.send(packet.buffer, options.id);
    if(!ret){
        throw new Error("fingerZazUart.send fail")
    }
}



/**
 * Receive data
 * @param {Object} params 
 * @param {number} params.prefix 
 * @param {number} params.cmd 
 * @param {number} params.len
 * @returns {Object} Returned data
 */
function receive(params) {
    // Retry 5 times to avoid the module failing to respond due to excessively fast reading.
    for(let i = 0; i < 5; i++){    
        const prefix1 = dxUart.receive(1, options.timeout, options.id);
        if (!prefix1 || (prefix1[0] !== 0xAA && prefix1[0] !== 0xA5)) {
            std.sleep(50)
            continue
        }

        const prefix2 = dxUart.receive(1, options.timeout, options.id);
        if (!prefix2 || (prefix2[0] !== 0x55 && prefix2[0] !== 0x5A)) {
            std.sleep(50)
            continue
        }

        // Define common fields
        let prefix, sid, did, cmd, len, ret, data, checksum;
        
        prefix = (prefix1[0] << 8) | prefix2[0];

        const value1 = dxUart.receive(6, options.timeout, options.id);
        sid = value1[0];
        did = value1[1];
        cmd = (value1[2] << 8) | value1[3];
        len = (value1[5] << 8) | value1[4];
        
        let value2 = dxUart.receive(len, options.timeout, options.id)
        if(value2 && value2.length === len){
            ret = value2[1] << 8 | value2[0];
            data = value2.subarray(2, len)
        } else {
            throw new Error("fingerZazUart.receive fail: data length mismatch")
        }

        
        let value3
        if(prefix1[0] === 0xAA && prefix2[0] === 0x55){
            // The check word is at the end.
            value3 = dxUart.receive((26 - 2 - 8 - len + 2), options.timeout, options.id)
            checksum = value3[value3.length - 2] | (value3[value3.length - 1] << 8);
        } else {
            // The check word comes first.
            value3 = dxUart.receive(2, options.timeout, options.id)
            checksum = value3[0] | (value3[1] << 8);
        }

        
        // Calculate the checksum (from prefix to data).
        let sum = prefix1[0];
        sum += prefix2[0];
        for (let i=0; i < value1.length; i++) sum += value1[i];
        for (let i=0; i < value2.length; i++) sum += value2[i];
        if ((sum & 0xFFFF) !== checksum) throw "Response Packet Checksum mismatch";

        if (params.prefix == prefix && params.cmd == cmd) {
            return {
                prefix: prefix,
                sid,
                did,
                command: cmd,
                length: len,
                ret: ret,
                data: Array.from(data),
                cks: true
            };
        } else {
            throw new Error("fingerZazUart.receive fail:")
        }
    }
}



export default dxFingerZaz;